package com.atguigu.yygh.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.exception.YyghException;
import com.atguigu.yygh.enums.OrderStatusEnum;
import com.atguigu.yygh.enums.PaymentStatusEnum;
import com.atguigu.yygh.hosp.client.ScheduleFeignClient;
import com.atguigu.yygh.model.order.OrderInfo;
import com.atguigu.yygh.model.order.PaymentInfo;
import com.atguigu.yygh.model.user.Patient;
import com.atguigu.yygh.mq.MqConst;
import com.atguigu.yygh.mq.RabbitService;
import com.atguigu.yygh.order.mapper.OrderInfoMapper;
import com.atguigu.yygh.order.service.OrderInfoService;
import com.atguigu.yygh.order.service.PaymentService;
import com.atguigu.yygh.order.service.WeiPayService;
import com.atguigu.yygh.order.utils.HttpRequestHelper;
import com.atguigu.yygh.user.client.PatientFeignClient;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.atguigu.yygh.vo.msm.MsmVo;
import com.atguigu.yygh.vo.order.OrderCountQueryVo;
import com.atguigu.yygh.vo.order.OrderCountVo;
import com.atguigu.yygh.vo.order.OrderMqVo;
import com.atguigu.yygh.vo.order.OrderQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2022-09-05
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private PatientFeignClient patientFeignClient;

    @Autowired
    private ScheduleFeignClient scheduleFeignClient;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private WeiPayService weiPayService;

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private PaymentService paymentService;

    @Override
    public Long submitOrder(String scheduleId, Long patientId) {
        // 提交订单的步骤：
        // 1.根据排班id获取医生详情信息
        ScheduleOrderVo scheduleOrderVo = scheduleFeignClient.getScheduleOrderVo(scheduleId);

        // 对当前时间进行判断是不是已经过了预约时间，如果过了时间就不让继续预约了
        if (new DateTime(scheduleOrderVo.getStopTime()).isBeforeNow()) {
            throw new YyghException(20001, "已过挂号时间，不能确认订单");
        }

        // 2.根据就诊人id获取就诊人详情信息
        Patient patient = patientFeignClient.getPatientById(patientId);

        Map<String, Object> paramMap = new HashMap<>();

        paramMap.put("hoscode", scheduleOrderVo.getHoscode());
        paramMap.put("depcode", scheduleOrderVo.getDepcode());
        paramMap.put("hosScheduleId", scheduleOrderVo.getHosScheduleId());
        paramMap.put("reserveDate", scheduleOrderVo.getReserveDate());
        paramMap.put("reserveTime", scheduleOrderVo.getReserveTime());
        paramMap.put("amount", scheduleOrderVo.getAmount());


        // 3.向第三方医院发送预约请求
        JSONObject response = HttpRequestHelper.sendRequest(paramMap, "http://localhost:9998/order/submitOrder");

        if (response.getInteger("code") == 200 && response != null) {
            //       3.2 如果预约数足够，预约成功，把排班详情，就诊人详情，预约信息插入order_info表中
            // 获取数据
            JSONObject data = response.getJSONObject("data");
            OrderInfo orderInfo = new OrderInfo();

            orderInfo.setHoscode(scheduleOrderVo.getHoscode());
            orderInfo.setDepcode(scheduleOrderVo.getDepcode());
            orderInfo.setUserId(patient.getUserId());
            orderInfo.setOutTradeNo(System.currentTimeMillis() + "" + new Random().nextInt(1000));
            orderInfo.setHosname(scheduleOrderVo.getHosname());
            orderInfo.setDepname(scheduleOrderVo.getDepname());
            orderInfo.setTitle(scheduleOrderVo.getTitle());
            orderInfo.setScheduleId(scheduleOrderVo.getHosScheduleId());
            orderInfo.setReserveDate(scheduleOrderVo.getReserveDate());
            orderInfo.setReserveTime(scheduleOrderVo.getReserveTime());

            // 就诊人
            orderInfo.setPatientId(patient.getId());
            orderInfo.setPatientName(patient.getName());
            orderInfo.setPatientPhone(patient.getPhone());

            // 第三方医院
            orderInfo.setHosRecordId(data.getString("hosRecordId"));
            orderInfo.setNumber(data.getInteger("number"));
            orderInfo.setFetchTime(data.getString("fetchTime"));
            orderInfo.setFetchAddress(data.getString("fetchAddress"));
            orderInfo.setAmount(scheduleOrderVo.getAmount());
            orderInfo.setQuitTime(scheduleOrderVo.getQuitTime());
            orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());

            // 添加到订单表中
            baseMapper.insert(orderInfo);

            //  3.3 返回预约成功后该医生（排班）信息最新剩余可预约数
            OrderMqVo orderMqVo = new OrderMqVo();
            orderMqVo.setScheduleId(scheduleId);
            Integer availableNumber = data.getInteger("availableNumber");
            orderMqVo.setAvailableNumber(availableNumber);
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(patient.getPhone());
            String templateCode = "你已经成功预约${time}时间的${name}医生，请按时就诊";
            msmVo.setTemplateCode(templateCode); // 设置模板
            Map<String, Object> msmMap = new HashMap<>();
            msmMap.put("time", scheduleOrderVo.getReserveDate());
            msmMap.put("name", scheduleOrderVo.getTitle());
            msmVo.setParam(msmMap);
            orderMqVo.setMsmVo(msmVo);
            // 把消息发送到rabbitmq
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
            //  3.4 发送短信提醒就诊人
            // 4.返回订单号
            return orderInfo.getId();
        } else {
            //       3.1 如果预约数不够，抛出异常
            throw new YyghException(20001, "号源已满");
        }


    }

    @Override
    public Page<OrderInfo> getOrderPage(Integer pageNum, Integer pageSize, Long userId, OrderQueryVo orderQueryVo) {
        Page<OrderInfo> page = new Page<>(pageNum, pageSize);
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        String orderStatus = orderQueryVo.getOrderStatus();
        String keyword = orderQueryVo.getKeyword(); // 医院名字
        Long patientId = orderQueryVo.getPatientId();
        String patientName = orderQueryVo.getPatientName();
        String createTimeBegin = orderQueryVo.getCreateTimeBegin();
        String createTimeEnd = orderQueryVo.getCreateTimeEnd();
        String reserveDate = orderQueryVo.getReserveDate();
        String outTradeNo = orderQueryVo.getOutTradeNo();
        if(!StringUtils.isEmpty(orderStatus)) {
            queryWrapper.eq("order_status", orderStatus);
        }
        if(!StringUtils.isEmpty(keyword)) {
            queryWrapper.like("hosname", keyword);
        }
        if(!StringUtils.isEmpty(patientId)) {
            queryWrapper.eq("patient_id", patientId);
        }
        if(!StringUtils.isEmpty(patientName)) {
            queryWrapper.like("patient_name", patientName);
        }
        if(!StringUtils.isEmpty(outTradeNo)) {
            queryWrapper.eq("out_trade_no", outTradeNo);
        }
        if(!StringUtils.isEmpty(reserveDate)) {
            queryWrapper.ge("reserve_date", reserveDate);
        }
        if(!StringUtils.isEmpty(createTimeBegin)) {
            queryWrapper.ge("create_time", createTimeBegin);
        }
        if(!StringUtils.isEmpty(createTimeEnd)) {
            queryWrapper.le("create_time", createTimeEnd);
        }
        Page<OrderInfo> orderPage = baseMapper.selectPage(page, queryWrapper);
        orderPage.getRecords().stream().forEach(item -> {
            this.packageOrderInfo(item);
        });
        return orderPage;
    }

    @Override
    public OrderInfo detail(Long orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        this.packageOrderInfo(orderInfo);
        return orderInfo;
    }

    @Override
    public void cancelOrder(Long orderId) {
        // 1. 确定当前取消预约的时间和挂号订单的取消预约时间截止时间对比，当前时间是否已经超过了挂号订单取消预约截止时间
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        DateTime quitTime = new DateTime(orderInfo.getQuitTime());
        if(quitTime.isBeforeNow()) {
            //         1.1如果超过了，抛出异常，不让用户取消
            throw new YyghException(20001, "已经超过了挂号订单取消预约截止时间");
        }
        // 2. 尚医通挂好平台请求第三方医院系统，通知第三方医院取消预约信息
        Map<String, Object> hospitalParamMap  = new HashMap<>();
        hospitalParamMap.put("hoscode", orderInfo.getHoscode());
        hospitalParamMap.put("hosRecordId", orderInfo.getHosRecordId());
        JSONObject response = HttpRequestHelper.sendRequest(hospitalParamMap, "http://localhost:9998/order/updateCancelStatus");
        //         2.1 第三方医院如果不同意取消，抛出异常，不能取消预约
        if(response == null || response.getInteger("code") != 200) {
            throw new YyghException(20001, "取消失败");
        }
        // 3. 判断用户此时的支付状态，是否已经支付挂号费
        if(orderInfo.getOrderStatus() == OrderStatusEnum.PAID.getStatus()){
            // 3.1 如果已经支付，退款
            boolean flag = weiPayService.refund(orderId);
            if (!flag) {
                throw new YyghException(20001, "取消失败");
            }
        }
        //  是否支付成功，都需要走下面的逻辑
        // 4. 更新订单的状态和支付记录表的状态
        orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
        orderInfoService.updateById(orderInfo);
        UpdateWrapper<PaymentInfo> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("order_id", orderId);
        updateWrapper.set("payment_status", PaymentStatusEnum.REFUND.getStatus());
        paymentService.update(updateWrapper);
        // 5. 更新医生的剩余可预约数信息
        // 6. 给就诊人短信提醒
        // 5,6点使用rabbitmq异步处理
        // 需要把更新的信息发乳消息队列中
        OrderMqVo orderMqVo = new OrderMqVo();
        orderMqVo.setScheduleId(orderInfo.getScheduleId()); // 设置排班id
        MsmVo msmVo = new MsmVo();
        msmVo.setPhone(orderInfo.getPatientPhone());
        msmVo.setTemplateCode("xxx");
        msmVo.setParam(null);
        orderMqVo.setMsmVo(msmVo);
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
    }

    @Override
    public void patientRemind() {
        // 统计所有当天订单
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("reserve_date", new DateTime().toString("yyyy-MM-dd"));
        queryWrapper.ne("order_status", OrderStatusEnum.CANCLE.getStatus());
        List<OrderInfo> orderInfos = baseMapper.selectList(queryWrapper);
        for (OrderInfo orderInfo : orderInfos) {
            // 给每个就诊人发送信息
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(orderInfo.getPatientPhone());
            String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
            }};
            msmVo.setParam(param);
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM_ITEM, msmVo);
        }
    }

    @Override
    public Map<String, Object> getCount(OrderCountQueryVo orderCountQueryVo) {
        List<OrderCountVo> orderCountVoList = baseMapper.getCount(orderCountQueryVo);
        // 抽取出日期数组和预约量数组
        List<String> dateList = orderCountVoList.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());
        List<Integer> countList = orderCountVoList.stream().map(OrderCountVo::getCount).collect(Collectors.toList());
        Map<String, Object> map = new HashMap<>();
        map.put("dateList",dateList);
        map.put("countList",countList);
        return map;
    }

    private void packageOrderInfo(OrderInfo item) {
        item.getParam().put("orderStatusString", OrderStatusEnum.getStatusNameByStatus(item.getOrderStatus()));
    }
}
